#include "graphgui.h"

#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>
#include <QPen>
#include <QStyleOptionGraphicsItem>
#include <QStyle>
#include <QGridLayout>
#include <QLabel>
#include <QFrame>
#include <QPaintEvent>

VertexGui::VertexGui(GraphGui *parent)
	: QGraphicsItem()
	, m_ttWidget(0)
{
	m_parent = parent;
	setFlag(ItemIsMovable);
	setFlag(ItemSendsGeometryChanges);
	setFlag(ItemIsSelectable);
	setZValue(1);
	setAcceptHoverEvents(true);
}

QRectF VertexGui::boundingRect() const
{
	qreal penWidth = 1;
	return QRectF(-6 - penWidth/2, -6 - penWidth/2, 12 + penWidth, 12 + penWidth);
}

QPainterPath VertexGui::shape() const
{
	QPainterPath path;
	path.addEllipse(boundingRect());
	return path;
}

void VertexGui::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
	Q_UNUSED(widget)
	painter->save();
	QPen p = painter->pen();
	p.setWidth(1);
	p.setBrush(Qt::black);
	painter->setPen(p);
	QPainterPath path;
	path.addEllipse(boundingRect());
	painter->drawPath(path);
	if (option->state & QStyle::State_Sunken) {
		painter->fillPath(path, Qt::lightGray);
	} else if (option->state & QStyle::State_Selected) {
		painter->fillPath(path, QColor(70, 120, 190));
	} else {
		painter->fillPath(path, Qt::black);
	}
	painter->restore();
}

QVariant VertexGui::itemChange(GraphicsItemChange change, const QVariant &value)
{
	if (change == ItemPositionChange && scene()) {
		// value is the new position.
		QPointF newPos = value.toPointF();
		QRectF rect = scene()->sceneRect();
		if (!rect.contains(newPos)) {
			// Keep the item inside the scene rect.
			newPos.setX(qMin(rect.right() - 7, qMax(newPos.x(), rect.left() + 7)));
			newPos.setY(qMin(rect.bottom() - 7, qMax(newPos.y(), rect.top() + 7)));
			return newPos;
		}
	}
	if (change == ItemPositionHasChanged && m_parent) {
		m_parent->vertexMoved(this);
	}
	return QGraphicsItem::itemChange(change, value);
}

void VertexGui::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
	update();
	QGraphicsItem::mousePressEvent(event);
}

void VertexGui::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
	update();
	QGraphicsItem::mouseReleaseEvent(event);
}

void VertexGui::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
	QGraphicsItem::hoverEnterEvent(event);
	if (m_ttWidget) {
		m_ttWidget->hide();
		delete m_ttWidget;
		m_ttWidget = 0;
	}
	QPoint p(event->screenPos().x() + 16, event->screenPos().y() + 16);
	m_ttWidget = new ToolTipWidget(associatedVertex(), event->widget());
	m_ttWidget->move(event->widget()->mapFromGlobal(p));
	m_ttWidget->show();
}

void VertexGui::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
	QGraphicsItem::hoverEnterEvent(event);
	if (m_ttWidget) {
		m_ttWidget->hide();
		delete m_ttWidget;
		m_ttWidget = 0;
	}
}

void VertexGui::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
	QGraphicsItem::hoverMoveEvent(event);
	if (m_ttWidget) {
		QPoint p(event->screenPos().x() + 16, event->screenPos().y() + 16);
		m_ttWidget->move(event->widget()->mapFromGlobal(p));
	}
}

Vertex VertexGui::associatedVertex() const
{
	return m_parent->vertexFromItem(this);
}

EdgeGui::EdgeGui(GraphGui *parent)
	: QGraphicsItem()
	, m_ttWidget(0)
	, m_color(Qt::black)
{
	m_parent = parent;
	setFlag(ItemIsSelectable);
	setZValue(-1);
	m_penWidth = 1.0;
	setAcceptHoverEvents(true);
}

void EdgeGui::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
	QGraphicsItem::hoverEnterEvent(event);
	if (m_ttWidget) {
		m_ttWidget->hide();
		delete m_ttWidget;
		m_ttWidget = 0;
	}
	QPoint p(event->screenPos().x() + 16, event->screenPos().y() + 16);
	m_ttWidget = new ToolTipWidget(associatedEdge(), event->widget());
	m_ttWidget->move(event->widget()->mapFromGlobal(p));
	m_ttWidget->show();
}

void EdgeGui::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
	QGraphicsItem::hoverEnterEvent(event);
	if (m_ttWidget) {
		m_ttWidget->hide();
		delete m_ttWidget;
		m_ttWidget = 0;
	}
}

void EdgeGui::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
	QGraphicsItem::hoverMoveEvent(event);
	if (m_ttWidget) {
		QPoint p(event->screenPos().x() + 16, event->screenPos().y() + 16);
		m_ttWidget->move(event->widget()->mapFromGlobal(p));
	}
}

void EdgeGui::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
	Q_UNUSED(widget)
	if (m_line.isNull())
		return;
	painter->save();
	if (option->state & QStyle::State_Selected) {
		QPen pen = painter->pen();
		pen.setWidthF(2.0);
		m_penWidth = 2.0;
		pen.setColor(QColor(70, 120, 190));
		painter->setPen(pen);
	} else {
		QPen pen = painter->pen();
		pen.setWidthF(1.0);
		pen.setColor(m_color);
		m_penWidth = 1.0;
		painter->setPen(pen);
	}
	painter->drawLine(m_line);
	painter->restore();
}

QRectF EdgeGui::boundingRect() const
{
	return shape().controlPointRect();
}

void EdgeGui::setLine(const QLineF &line)
{
	if (m_line == line)
		return;
	prepareGeometryChange();
	m_line = line;
	update();
}

void EdgeGui::setColor(const QColor &color)
{
	m_color = color;
	update();
}

QPainterPath EdgeGui::shape() const
{
	QPainterPath path;
	if (m_line == QLineF())
		return path;
	path.moveTo(m_line.p1());
	path.lineTo(m_line.p2());

	QPainterPathStroker stroker;
	stroker.setWidth(m_penWidth);
	return stroker.createStroke(path);
}

Edge EdgeGui::associatedEdge() const
{
	return m_parent->edgeFromItem(this);
}

GraphGui::GraphGui(Graph graph, QObject *parent)
	: QGraphicsScene(QRectF(-500, -300, 1000, 600), parent)
	, m_graph(graph)
{
	drawGraph();
}

GraphGui::GraphGui(Graph graph, const QRectF &sceneRect, QObject *parent)
	: QGraphicsScene(sceneRect, parent)
	, m_graph(graph)
{
	drawGraph();
}

void GraphGui::colorEdges(QList<Edge>edges, const QColor &color)
{
	foreach (Edge e, edges) {
		EdgeGui *item = m_edges.value(e, 0);
		if (item)
			item->setColor(color);
	}
}

void GraphGui::colorPath(Path p, const QColor &color)
{
	colorEdges(p.edgeSequence(), color);
}

void GraphGui::drawGraph()
{
	for (QHash<Vertex, VertexGui *>::iterator i = m_vertices.begin(); i != m_vertices.end(); ++i) {
		delete i.value();
	}
	for (QHash<Edge, EdgeGui *>::iterator i = m_edges.begin(); i != m_edges.end(); ++i) {
		delete i.value();
	}

	QList<Vertex> vertices = m_graph.vertices();
	foreach (Vertex v, vertices) {
		QPointF vpos(qrand() % int(sceneRect().width() - 20) + (sceneRect().left() + 10),
					 qrand() % int(sceneRect().height() - 20) + (sceneRect().top() + 10));
		VertexGui *vertexItem = new VertexGui(this);
		addItem(vertexItem);
		vertexItem->setPos(vpos);
		m_vertices.insert(v, vertexItem);
	}

	QList<Edge> edges = m_graph.edges();
	foreach (Edge e, edges) {
		QPointF p1 = m_vertices.value(e.u())->pos();
		QPointF p2 = m_vertices.value(e.v())->pos();
		EdgeGui *edgeItem = new EdgeGui(this);
		edgeItem->setLine(QLineF(p1, p2));
		addItem(edgeItem);
		m_edges.insert(e, edgeItem);
	}
}

void GraphGui::vertexMoved(VertexGui *vg)
{
	Vertex v = m_vertices.key(vg, Vertex());
	if (v.isNull()) {
		return;
	}

	QList<Edge> edges = m_graph.edges(v);
	foreach (Edge e, edges) {
		EdgeGui *eg = m_edges.value(e, 0);
		if (eg) {
			QPointF p1 = m_vertices.value(e.u())->pos();
			QPointF p2 = m_vertices.value(e.v())->pos();
			eg->setLine(QLineF(p1, p2));
		} else {
			qWarning("Graph has an edge but no edge gui item! Weird...");
		}
	}
}

Vertex GraphGui::vertexFromItem(const VertexGui *item) const
{
	return m_vertices.key(const_cast<VertexGui *>(item), Vertex());
}

Edge GraphGui::edgeFromItem(const EdgeGui *item) const
{
	return m_edges.key(const_cast<EdgeGui *>(item), Edge());
}

ToolTipWidget::ToolTipWidget(Vertex v, QWidget *parent)
	: QWidget(parent)
{
	vertex = v;
	type = VertexType;
	initialize();
}

ToolTipWidget::ToolTipWidget(Edge e, QWidget *parent)
	: QWidget(parent)
{
	edge = e;
	type = EdgeType;
	initialize();
}

void ToolTipWidget::initialize()
{
//	setAutoFillBackground(true);
	QGridLayout *grid = new QGridLayout;
	QLabel *title = new QLabel(type == EdgeType ? trUtf8("<b>Edge</b>") : trUtf8("<b>Vertex</b>"), this);
	grid->addWidget(title, 0, 0, 1, 2, Qt::AlignCenter);
	QFrame *line = new QFrame(this);
	line->setFrameShape(QFrame::HLine);
	line->setFrameShadow(QFrame::Sunken);
	grid->addWidget(line, 1, 0, 1, 2);

	if (type == VertexType) {
		grid->addWidget(new QLabel(trUtf8("Name:"), this), 2, 0);
		grid->addWidget(new QLabel(vertex.name(), this), 2, 1);
	} else {
		grid->addWidget(new QLabel(trUtf8("U name:"), this), 2, 0);
		grid->addWidget(new QLabel(edge.u().name(), this), 2, 1);
		grid->addWidget(new QLabel(trUtf8("V name:"), this), 3, 0);
		grid->addWidget(new QLabel(edge.v().name(), this), 3, 1);
		grid->addWidget(new QLabel(trUtf8("Length:"), this), 4, 0);
		grid->addWidget(new QLabel(QString::number(edge.length()), this), 4, 1);
	}
	setLayout(grid);
}

void ToolTipWidget::paintEvent(QPaintEvent *event)
{
	QPainter p(this);
	QRect r(QPoint(0, 0), size());
	p.fillRect(r, QColor(240, 240, 200));
	p.drawRect(r.adjusted(0, 0, -1, -1));
	QWidget::paintEvent(event);
}
